package main

import (
	"encoding/json"
	"fmt"
	"os"
	"regexp"
	"strings"

	md "github.com/JohannesKaufmann/html-to-markdown"
	"github.com/gomarkdown/markdown"
	"github.com/gomarkdown/markdown/parser"
)

// 分片文本的最大长度
const SliceTextMaxLength = 500

// 分片分隔符
const sliceSeparator = "\n\n----####====\n\n"

// 预编译正则表达式
var (
	// 标题匹配正则
	HeadingRegex = regexp.MustCompile(`<h([1-6])[^>]*>(.*?)</h[1-6]>`)
	// 条款匹配正则
	ProvisionRegex = regexp.MustCompile(`(?:^|\n)(第[0-9一二三四五六七八九十零百千]+条)`)
	// 连续换行符
	NewlineRegex = regexp.MustCompile(`\n{3,}`)
	// 空格匹配
	SpaceRegex = regexp.MustCompile(`[ \t]+`)
)

// MarkdownFragment 表示分片后的文本片段
type MarkdownFragment struct {
	Title   string `json:"title"`
	Content string `json:"content"`
}

// MarkdownSlicer 实现Markdown文本分片功能
// markdownContent markdown格式文本字符串
// isProvision 是否是规则制度条款，如果为true则会按条款进行分片
func MarkdownSlicer(markdownContent string, isProvision bool) ([]MarkdownFragment, error) {
	// 将Markdown转换为HTML
	// 转换成HTML之后，标签说明：
	// 标题：<h1>、<h2>、<h3>、<h4>、<h5>、<h6>
	// 段落：<p>
	// 表格：<table>
	// 列表: <ul>、<ol>
	// 代码块：<pre><code>
	parser := parser.NewWithExtensions(parser.CommonExtensions | parser.AutoHeadingIDs)
	htmlContent := markdown.ToHTML([]byte(markdownContent), parser, nil)

	// 基于HTML进行分片
	fragments, err := sliceHTML(string(htmlContent))
	if err != nil {
		return nil, err
	}

	// 不再使用转换器处理标题，直接使用构建好的Markdown格式标题
	converter := md.NewConverter("", true, nil)

	var newFragments []MarkdownFragment
	for i := range fragments {
		fmt.Println(">>>> " + fragments[i].Content)
		contentMarkdown, err := converter.ConvertString(fragments[i].Content)
		if err != nil {
			return nil, err
		}

		if isProvision {
			// 规则制度条款处理
			provisions, err := sliceProvision(contentMarkdown)
			if err != nil {
				return nil, err
			}
			for _, provision := range provisions {
				newFragments = append(newFragments, MarkdownFragment{
					Title:   fragments[i].Title,
					Content: provision,
				})
			}
		} else if len(contentMarkdown) < SliceTextMaxLength {
			// 文本没有超出最大长度，直接添加
			newFragments = append(newFragments, MarkdownFragment{
				Title:   fragments[i].Title,
				Content: contentMarkdown,
			})
		} else {
			// 如果转换后的文本长度超出某个值，则根据HTML文本的段落信息等做进一步的分片
			// 段落、表格、代码快、列表等，通常不应该从中分拆
			paragraphs, err := sliceHTMLParagraph(contentMarkdown)
			if err != nil {
				return nil, err
			}
			for _, paragraph := range paragraphs {
				newFragments = append(newFragments, MarkdownFragment{
					Title:   fragments[i].Title,
					Content: paragraph,
				})
			}
		}
	}

	return newFragments, nil
}

// sliceProvision 条款分片
// 条款分片规则：
// 1. 条款标题格式：第1条、第2条、第3条等
// 2. 条款内容：条款标题之后的内容，直到下一个条款标题之前的内容
func sliceProvision(content string) (fragments []string, err error) {
	matches := ProvisionRegex.FindAllStringIndex(content, -1)
	if len(matches) == 0 {
		fragments = append(fragments, content)
		return
	}
	// fmt.Printf("\n===> slice provision matches: %+v\n", matches)

	var sliceStarts []int
	for i := range matches {
		sliceStarts = append(sliceStarts, matches[i][0])
	}

	// 提取条款内容
	for i := 0; i < len(sliceStarts); i++ {
		var start = sliceStarts[i]
		var end int
		if i == len(sliceStarts)-1 {
			end = len(content)
		} else {
			end = sliceStarts[i+1]
		}
		fragments = append(fragments, strings.TrimSpace(content[start:end]))
	}
	return
}

// sliceHTMLParagraph
// 经过sliceHTML分片之后，如果转换后的文本长度超出某个值，则根据HTML文本的段落信息等做进一步的分片
// 段落、表格、代码快、列表等，通常不应该从中分拆。以下是标签说明:
// 段落：<p>
// 表格：<table>
// 列表: <ul>、<ol>
// 代码块：<pre><code>
func sliceHTMLParagraph(htmlContent string) (fragments []string, err error) {
	return
}

// sliceHTML 基于HTML进行文本分片
func sliceHTML(htmlContent string) ([]MarkdownFragment, error) {
	// 提取所有标题和内容
	matches := HeadingRegex.FindAllStringSubmatch(htmlContent, -1)

	if len(matches) == 0 {
		// 如果没有标题，整个内容作为一个片段
		content := cleanContent(htmlContent)
		return []MarkdownFragment{
			{Title: "", Content: content},
		}, nil
	}

	var fragments []MarkdownFragment
	var currentHeadings []string

	// 处理每个标题
	for i := range matches {
		hLevel := matches[i][1]
		hText := matches[i][2]

		// 构建当前标题路径
		level := parseInt(hLevel)
		if level > len(currentHeadings) {
			// 如果当前标题级别比当前路径长，补充空标题
			for j := len(currentHeadings); j < level-1; j++ {
				currentHeadings = append(currentHeadings, "")
			}
			currentHeadings = append(currentHeadings, hText)
		} else if level == len(currentHeadings) {
			// 如果当前标题级别与当前路径长度相同，替换最后一个标题
			currentHeadings[level-1] = hText
		} else {
			// 如果当前标题级别比当前路径短，截断路径并替换
			currentHeadings = currentHeadings[:level-1]
			currentHeadings = append(currentHeadings, hText)
		}

		// 获取当前标题到下一个标题之间的内容
		var nextHeadingPos int
		if i < len(matches)-1 {
			nextHeadingPos = strings.Index(htmlContent, matches[i+1][0])
		} else {
			nextHeadingPos = len(htmlContent)
		}
		currentHeadingPos := strings.Index(htmlContent, matches[i][0]) + len(matches[i][0])
		content := htmlContent[currentHeadingPos:nextHeadingPos]
		content = cleanContent(content)

		// 构建完整的标题路径
		var titlePath strings.Builder
		var sep = "" // 标题之间的分隔符，初始为空
		for j, heading := range currentHeadings {
			if heading == "" {
				continue
			}
			// 为每个标题确定其实际的级别（从1开始）
			// 当前标题的级别应该是j+1，因为currentHeadings数组中索引从0开始
			level := j + 1
			// 根据标题级别添加#
			titlePath.WriteString(sep)
			titlePath.WriteString(strings.Repeat("#", level))
			titlePath.WriteString(" ")
			titlePath.WriteString(heading)
			sep = "\n"
		}

		fragments = append(fragments, MarkdownFragment{
			Title:   titlePath.String(),
			Content: content,
		})
	}

	return fragments, nil
}

// cleanContent 清理内容，移除标题和多余空白，但保留段落换行
func cleanContent(content string) string {
	// 移除标题标签
	content = HeadingRegex.ReplaceAllString(content, "")

	// 处理空白字符，保留换行符
	// 首先将多个连续的换行符替换为两个换行符（表示段落分隔）
	content = NewlineRegex.ReplaceAllString(content, "\n\n")

	// 然后将行内的多余空白字符（除换行符外）替换为单个空格
	content = SpaceRegex.ReplaceAllString(content, " ")

	// 最后移除首尾空白字符
	content = strings.TrimSpace(content)
	return content
}

// parseInt 将字符串转换为整数
func parseInt(s string) int {
	var result int
	fmt.Sscanf(s, "%d", &result)
	return result
}

func main() {
	// 读取命令行参数
	if len(os.Args) < 2 {
		fmt.Println("Usage: md-slicer <markdown_file>")
		os.Exit(1)
	}

	filePath := os.Args[1]

	// 读取Markdown文件
	content, err := os.ReadFile(filePath)
	if err != nil {
		fmt.Printf("Failed to read file: %v\n", err)
		os.Exit(1)
	}

	// 分片处理（按规章制度条款拆分）
	fragments, err := MarkdownSlicer(string(content), true)
	if err != nil {
		fmt.Printf("Failed to slice markdown: %v\n", err)
		os.Exit(1)
	}

	// 输出JSON格式结果
	jsonBytes, err := json.MarshalIndent(fragments, "", "  ")
	if err != nil {
		fmt.Printf("Failed to marshal JSON: %v\n", err)
		os.Exit(1)
	}

	fmt.Println(string(jsonBytes))

	// 增加特殊分片标识，合成最后的文本
	var sliceContents []string
	for _, fragment := range fragments {
		if fragment.Content == "" {
			continue
		}
		combined := fmt.Sprintf("%s\n%s", fragment.Title, fragment.Content)
		sliceContents = append(sliceContents, combined)
	}
	combinedContent := strings.Join(sliceContents, sliceSeparator)
	// 写入文件
	err = os.WriteFile(filePath+".combined.txt", []byte(combinedContent), 0644)
	if err != nil {
		fmt.Printf("Failed to write file: %v\n", err)
		os.Exit(1)
	}
}
